package ms.jacrim.pddl;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ms.jacrim.tropos.*;
import ms.jacrim.tropos.TroposObject;
import ms.jacrim.tropos.Service.ServiceType;

public class PddlFactory {

	@Retention(value = RetentionPolicy.RUNTIME)
	@Target(value = { ElementType.METHOD })
	static @interface RelationshipImporter {
		String Type();
	}
	
	private List<Diagram> diagrams;
	private Map<String, Method> relationshipImporter = new HashMap<String, Method>();

	public PddlFactory() throws Exception {
		this.diagrams = new ArrayList<Diagram>();

		RelationshipImporter t;
		for(Method m: PddlFactory.class.getMethods())
			if ( (t = m.getAnnotation(RelationshipImporter.class)) != null)
				relationshipImporter.put(t.Type().toUpperCase(), m);
		
//		relationshipImporter.put(Object.OWNERSHIP, PddlFactory.class.getMethod("importOwnership", PddlProblem.class, Relationship.class));
//		relationshipImporter.put(Object.ASSOCIATION, PddlFactory.class
//				.getMethod("importAssociation", PddlProblem.class, Relationship.class));
	}

	public int Count() {
		return diagrams.size();
	}
	
	public void addDiagram(Diagram diagram) {
		if (diagram != null)
			diagrams.add(diagram);
	}

	public int DiagramCount() {
		return diagrams.size();
	}
	
	public Diagram Diagram(int index) {
		return diagrams.get(index);
	}
	
	public PddlProblem generatePddlProblemFile() throws Exception {
		PddlProblem result = new PddlProblem();

		for (Diagram d : diagrams) {
			List<Actor> actors = d.getObjects(Actor.class);
			for (Actor a : actors)
				result.addObject(a.getNormalizeName(), "t_actor");

			importActors(result, actors);

			importRelationship(result, d.getObjects(Relationship.class));
		}

		return result;
	}

	private void importRelationship(PddlProblem result, List<Relationship> relations) throws Exception {
		for (Relationship r : relations) {
			Method importer = relationshipImporter.get(r.getType());
			if (importer != null)
				importer.invoke(this, result, r);
		}
	}

	private void importActors(PddlProblem result, List<Actor> actors) {
		List<String> subGoals = new ArrayList<String>();
		for (Actor a : actors)
			for (Service s : a.getServices())
				if (s.getServiceType() == ServiceType.GOAL) {
					String normalizedName = s.getNormalizeName();
					result.addObject(normalizedName, "t_goal");
					// suppose this is a top goal
					result.addGoal(normalizedName);
					// find its composition
					List<Composition> composition = s.getCompositions();
					if (composition.size() > 0) {
						String predicate = composition.get(0).getCompositionType().toString().toLowerCase() + "_subgoal"
								+ composition.size();
						PddlProblem.PddlPredicate pred = result.addFact(predicate, normalizedName);

						for (Composition c : composition)
							if (c.getTarget() != null) {
								pred.getArguments().add(c.getTarget().getNormalizeName());
								subGoals.add(c.getTarget().getNormalizeName());
							}
					}
				}
		for (String sg : subGoals)
			result.removeGoal(sg);
	}

	@RelationshipImporter(Type = TroposObject.OWNERSHIP)
	public void importOwnership(PddlProblem result, Relationship relation) {
		String predicate = null;
		Ownership o = (Ownership) relation;

		switch (o.getOwnershipType()) {
		case OWN:
			break;
		case REQUEST:
			predicate = "requests";
			break;
		case PROVIDE:
			predicate = "can_provide";
			break;
		}

		if (predicate != null)
			result.addFact(predicate, relation.getSource().getNormalizeName(), relation.getTarget().getNormalizeName());
	}

	@RelationshipImporter(Type = TroposObject.ASSOCIATION)
	public void importAssociation(PddlProblem result, Relationship relation) {
		String predicate = null;
		Association r = (Association) relation;

		switch (r.getAssociationType()) {
		case COVER:
			predicate = "can_depend_on";
		}

		if (predicate != null)
			result.addFact(predicate, relation.getSource().getNormalizeName(), relation.getTarget().getNormalizeName());

	}
}
